{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# FLASK 中的上下文语境\r\n",
    "\r\n",
    "使用上下文语境(contexts)让某些变量可以暂时全局访问。\r\n",
    "\r\n",
    "如果您来自 `Django` 背景，那么您可能已经注意到 `Flask` 中的 `view` 函数不接受 `request` 作为第一个参数。在 `Flask` 中，我们使用 `request` 对象访问传入请求中的数据，如下所示:\r\n",
    "\r\n",
    "```python\r\n",
    "from flask import Flask, request\r\n",
    "\r\n",
    "@app.route('/')\r\n",
    "def requestdata():\r\n",
    "    return \"Hello! Your IP is {} and you are using {}: \".format(request.remote_addr, request.user_agent)\r\n",
    "```\r\n",
    "\r\n",
    "上面的代码可能会给您一个印象，即 `request` 是一个全局对象，实际上它不是。如果 `request` 是一个全局对象，那么在一个多线程程序中，我们的应用程序将无法区分两个同时发出的请求，因为一个多线程程序共享线程中的所有变量。使用一种叫做 `Contexts` 的东西来使某些变量像全局变量一样工作，当你访问它们的时候，你就可以访问当前线程的对象。在术语集中，这样的变量被称为  `thread-locals` 线程局部变量。\r\n",
    "\r\n",
    "## Flask 提供了两种 `Context` 上下文语境:\r\n",
    "\r\n",
    "1. 应用程序上下文 Application Context.\r\n",
    "2. 请求上下文 Request Context. \r\n",
    "\r\n",
    "应用程序上下文用于存储与应用程序通用的值，如数据库连接、配置等; 而请求上下文用于存储特定于每个请求的值。\r\n",
    "\r\n",
    "应用程序上下文公开像 `current_app` 和 `g` 这样的对象。 `current_app` 指向处理请求的实例，而`g` 用于在请求处理期间临时存储数据。一旦设置了一个值，您就可以在任何视图函数中访问它。存储在 `g` 中的数据在每次请求后重置。\r\n",
    "\r\n",
    "与应用程序上下文一样，请求上下文也公开 `request` 和 `session` 等对象。如前所述， `request` 对象包含有关当前 web 请求的信息， `session` 是一个类似字典的对象，用于存储在请求之间持久存储的值。\r\n",
    "\r\n",
    "Flask 在收到请求时激活（或推送）应用程序和请求上下文，并在处理完请求时删除它们。\r\n",
    "当应用程序上下文被推入时，它所公开的所有变量对线程来说都是可用的。\r\n",
    "类似地，当请求上下文被推入时，它所公开的所有变量对线程来说都是可用的。\r\n",
    "在视图函数中，您可以访问应用程序和请求上下文公开的所有对象，因此不必担心应用程序或请求上下文是否处于活动状态。\r\n",
    "但是，如果您尝试在视图函数之外或在 pythonshell 中访问这些对象，则会出现错误。下面的 shell 会话演示了这一点:\r\n"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "source": [
    "# 如果您尝试在 视图函数 `a view function` 之外或在 `pythonshell` 中访问这些对象，则会出现错误。\r\n",
    "from flask import Flask, request, current_app\r\n",
    "request.method   # get the request method "
   ],
   "outputs": [
    {
     "output_type": "error",
     "ename": "RuntimeError",
     "evalue": "Working outside of request context.\n\nThis typically means that you attempted to use functionality that needed\nan active HTTP request.  Consult the documentation on testing for\ninformation about how to avoid this problem.",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_15332/2204579228.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;31m# 如果您尝试在 视图函数 `a view function` 之外或在 `pythonshell` 中访问这些对象，则会出现错误。\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[1;32mfrom\u001b[0m \u001b[0mflask\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mFlask\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mrequest\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcurrent_app\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[0mrequest\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmethod\u001b[0m   \u001b[1;31m# get the request method\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32mE:\\PYTHON39\\lib\\site-packages\\werkzeug\\local.py\u001b[0m in \u001b[0;36m__getattr__\u001b[1;34m(self, name)\u001b[0m\n\u001b[0;32m    345\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[0mname\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;34m\"__members__\"\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    346\u001b[0m             \u001b[1;32mreturn\u001b[0m \u001b[0mdir\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_get_current_object\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 347\u001b[1;33m         \u001b[1;32mreturn\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_get_current_object\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mname\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    348\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    349\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__setitem__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mE:\\PYTHON39\\lib\\site-packages\\werkzeug\\local.py\u001b[0m in \u001b[0;36m_get_current_object\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    304\u001b[0m         \"\"\"\n\u001b[0;32m    305\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__local\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"__release_local__\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 306\u001b[1;33m             \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__local\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    307\u001b[0m         \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    308\u001b[0m             \u001b[1;32mreturn\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__local\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__name__\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mE:\\PYTHON39\\lib\\site-packages\\flask\\globals.py\u001b[0m in \u001b[0;36m_lookup_req_object\u001b[1;34m(name)\u001b[0m\n\u001b[0;32m     36\u001b[0m     \u001b[0mtop\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0m_request_ctx_stack\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtop\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     37\u001b[0m     \u001b[1;32mif\u001b[0m \u001b[0mtop\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 38\u001b[1;33m         \u001b[1;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0m_request_ctx_err_msg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     39\u001b[0m     \u001b[1;32mreturn\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtop\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mname\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     40\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mRuntimeError\u001b[0m: Working outside of request context.\n\nThis typically means that you attempted to use functionality that needed\nan active HTTP request.  Consult the documentation on testing for\ninformation about how to avoid this problem."
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "`request.method` 返回请求中使用的 HTTP 方法，但是由于没有实际的 HTTP 请求，请求上下文是未激活的。\r\n",
    "\r\n",
    "我们可以使用 Flask 实例的 `app_context()` 方法创建应用程序上下文。"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "source": [
    "from main2 import app\r\n",
    "from flask import Flask, request, current_app\r\n",
    "\r\n",
    "app_context = app.app_context()\r\n",
    "app_context.push()\r\n",
    "\r\n",
    "current_app.name"
   ],
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "'main2'"
      ]
     },
     "metadata": {},
     "execution_count": 3
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "前面的代码可以使用 `with` 语句简化如下:"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "source": [
    "from main2 import app\r\n",
    "from flask import Flask, request, current_app\r\n",
    "\r\n",
    "with app.app_context():   #With 语句是创建上下文的首选方法。\r\n",
    "    print(current_app.name)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "main2\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "With 语句是创建上下文的首选方法。\r\n",
    "\r\n",
    "类似地，我们可以使用 Flask 实例的 `test_request_context()`方法创建请求上下文。需要记住的重要一点是，每当推送请求上下文时，应用程序上下文即被创建(如果尚未存在的话)。下面的 shell 会话演示了如何创建请求上下文:"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "source": [
    "from main2 import app\r\n",
    "from flask import Flask, request, current_app\r\n",
    "\r\n",
    "with app.test_request_context('/products'):  # The URL /products is arbitrarily chosen 任意选择的.\r\n",
    "    print(request.path)\r\n",
    "    print(request.method)\r\n",
    "    print(current_app.name)"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "/products\n",
      "GET\n",
      "main2\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "URL链接 `/products` 是任意选择的\r\n",
    "\r\n",
    "这就是你需要知道的关于 Flask 上下文的全部内容。"
   ],
   "metadata": {}
  }
 ],
 "metadata": {
  "orig_nbformat": 4,
  "language_info": {
   "name": "python",
   "version": "3.9.6",
   "mimetype": "text/x-python",
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "pygments_lexer": "ipython3",
   "nbconvert_exporter": "python",
   "file_extension": ".py"
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3.9.6 64-bit"
  },
  "interpreter": {
   "hash": "c644d696b95f5e0f4df3c6556741cf30bcf9ea6ca93c3e1f29fcf31d885534fc"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}